package toy.keli.picmap.model.types;

import android.os.Build;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import androidx.annotation.RequiresApi;

public class T {

    private enum Types{IS_NULL,IS_BOOL,IS_INT,IS_LONG,IS_DOUBLE,IS_STRING,IS_ARRAY,IS_OBJECT}
//    private final char IS_NULL = 0;
//    private final char IS_BOOL = 1;
//    private final char IS_INT = 2;
//    private final char IS_LONG = 3;
//    private final char IS_DOUBLE = 4;
//    private final char IS_STRING = 5;
//
//    private final char IS_ARRAY = 6;
//    private final char IS_OBJECT = 7;

    private Types type;
    private zend_val value;

    public  T(){
        this.type= Types.IS_NULL;
        this.value=new zend_val();
    }
    public  T(boolean i){
        this.type=Types.IS_BOOL;
        this.value=new zend_val(i);
    }
    public  T(int i){
        this.type=Types.IS_INT;
        this.value=new zend_val(i);
    }
    public  T(float i){
        this.type=Types.IS_LONG;
        this.value=new zend_val(i);
    }
    public  T(double i){
        this.type=Types.IS_DOUBLE;
        this.value=new zend_val(i);
    }
    public  T(String i){
        this.type=Types.IS_STRING;
        this.value=new zend_val(i);
    }
    public  T(Object i){
        this.type=Types.IS_OBJECT;
        this.value=new zend_val(i);
    }

    public boolean push(T v)  {
        if(this.type==Types.IS_NULL){
            this.type=Types.IS_ARRAY;
        }
        if(this.type!=Types.IS_ARRAY) return false; //throw new Exception("类型配置错误,变量是"+this.typeName()+"类型,无法push,只有数组类型才能push");
        return this.value.aval.add(v);
    }

    public boolean set(String key,T val) {
        if(this.type==Types.IS_NULL){
            this.type = Types.IS_OBJECT;
        }
        if(this.type!=Types.IS_OBJECT) return false; //throw new Exception("类型配置错误,变量是"+this.typeName()+"类型,无法set,只有hash类型才能set");
        this.value.hmval.put(key,val);
        return true;
    }
    public boolean set(String key,Object val) {
        return this.set(key,T.valueOf(val));
    }

    public T get(String key) {
        if(this.value.hmval ==null && this.value.oval!=null)
            return new Reflection(this.value.oval).getField(key);
        return this.value.hmval.get(key);
    }

    public T pop() {
        int i=this.value.aval.size()-1;
        T rt= this.value.aval.get(i);
        this.value.aval.remove(i);
        return rt;
    }

    public T shift() {
        T rt=this.value.aval.get(0);
        this.value.aval.remove(0);
        return rt;
    }

    public boolean unset(String key){
        this.value.hmval.remove(key);
        return true;
    }

    public boolean unset(int i){
        this.value.aval.remove(i);
        return true;
    }

    //functionCaseClass.funcTest((arg)->System.out.println("a   "+arg));
    public void forEach(IFunction func){
        if(this.type==Types.IS_ARRAY) {
            for (int i = 0; i < this.value.aval.size(); i++)
                func.call(this.value.aval.get(i), String.valueOf(i));
        }else if(this.type==Types.IS_OBJECT) {
            for (String key : this.value.hmval.keySet()){
                func.call(this.value.hmval.get(key),key);
            }
        }
    }

    private String typeName() {
        String map[]={"null","bool","int","long","double","string","array","hash"};
        return map[type.ordinal()];
    }

    public zend_val getValue() {
        return this.value;
    }

    public String value(){
        return this.toString();
    }

    public Object source(){
        return value.get(type.ordinal());
    }

    public int toInt(){
        return Integer.valueOf(this.toString()).intValue();
    }

    public float toFloat(){
        return Double.valueOf(this.toString()).intValue();
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    public Map<String,?> toHash(){
        HashMap<String,String> rt = new HashMap<String ,String>();
        if(this.type==Types.IS_OBJECT){
            try {
                Reflection r=new Reflection(this.value.oval);
                String[] attrs=r.getFields();
                Class C=this.value.oval.getClass();

                for(int i=0;i<attrs.length;i++) {
                    String key=attrs[i];
                    String val="";
                    Field field=C.getDeclaredField(key);
                    if(Modifier.isPublic(field.getModifiers())){
                        val=String.valueOf(field.get(this.value.oval));
                    }else {
                        String mname="get" + key.substring(0, 1).toUpperCase() + key.substring(1);
                        if(r.method_exists(mname)) {
                            val=r.method_call(mname).toString();
                        }else continue;
                    }
                    rt.put(key, val);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            return rt;
        }
        return this.value.hmval;
    }

    public Object to(Object v) {
        Object rt=this.source();
        if (v instanceof Integer) {
            return (int)rt;
        } else if (v instanceof String) {
            return (String) rt;
        } else if (v instanceof Double) {
            return  (Double)rt;
        } else if (v instanceof Float) {
            float f = (Float) rt;
            return new T(f);
        } else if (v instanceof Long) {
            long l = (Long) rt;
        } else if (v instanceof Boolean) {
            boolean b = (Boolean) rt;
            return new T(b);
        }

        return rt;
    }

    public static T valueOf(Object v){
        if (v instanceof Integer) {
            return new T((int)v);
        } else if (v instanceof String) {
            return new T((String) v);
        } else if (v instanceof Double) {
            double d = ((Double) v).doubleValue();
            return new T(d);
        } else if (v instanceof Float) {
            float f = ((Float) v).floatValue();
            return new T(f);
        } else if (v instanceof Long) {
            long l = ((Long) v).longValue();
            return new T(l);
        } else if (v instanceof Boolean) {
            boolean b = ((Boolean) v).booleanValue();
            return new T(b);
        } else if (v ==null || v=="") {
            return new T();
        } else if (v instanceof  T) {
            return (T) v;
        }
        return new T(v);
    }

    //数据类型的互相转换  https://www.cnblogs.com/mike-mei/p/11110051.html
    @Override
    public String toString() {
        switch (this.type.ordinal()) {
            case 0:
                return "null";
            case 1:
                return this.value.bval ? "1" : "0";
            case 2:
                return Integer.toString(this.value.ival);
            case 3:
            case 4:
                return Double.toString(this.value.dval);
            case 5:
                return this.value.sval;
            case 6:
                return "array(" + String.valueOf(this.value.aval.size()) + ")";
            case 7:
                if(this.value.hmval==null && this.value.oval!=null) return this.value.oval.toString();
                return "object(" + String.valueOf(this.value.hmval.size()) + ")";
        }

        return "";
    }

    public T get(int i) {
        return this.value.aval.get(i);
    }

    public void toObject(Object rt){
        Reflection r=new Reflection(rt);
        for(String k: this.value.hmval.keySet()) {
            r.setField(k,this.value.hmval.get(k));
        }
    }
    //匿名函数  https://www.jianshu.com/p/956a0464001c
    public interface IFunction{
        //    void call(var v,int i);
        void call(T value,String key);
    }

    class zend_val{
        boolean bval;
        int ival;
        double dval;
        String sval;
        Object oval;

        List <T>aval;
        Map<String,T> hmval;

        public zend_val(boolean b) {
            this.bval=b;
        }

        public zend_val(int i) {
            this.ival=i;
        }

        public zend_val(float i) {
            this.dval=i;
        }

        public zend_val(double i) {
            this.dval=i;
        }

        public zend_val(String i) {
            this.sval=i;
        }

        public zend_val(Object i) {
            this.oval=i;
        }
        public zend_val() {
            aval=new ArrayList<T>();
            hmval=new HashMap<String, T>();
        }

        public Object get(int ordinal) {
            //private enum Types{IS_NULL,IS_BOOL,IS_INT,IS_LONG,IS_DOUBLE,IS_STRING,IS_ARRAY,IS_OBJECT}
            Object[] rt={null,bval,ival,dval,dval,sval,aval,oval};
            if(ordinal==7 && hmval!=null) return hmval;
            return rt[ordinal];
        }
    }
}


